package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.promotion.domain.dto.UserCouponDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.UserCouponQuery;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.ExchangeCodeStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.tianji.promotion.utils.CodeUtil;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.tianji.common.constants.CacheConstant.*;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author xjs
 */
@Service
public class UserCouponServiceImpl2 extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {

    @Autowired
    @Lazy
    private ICouponService couponService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private IExchangeCodeService codeService;
    @Autowired
    private CouponMapper couponMapper;
    @Autowired
    private PlatformTransactionManager txManager;
    @Autowired
    private RabbitMqHelper mqHelper;
    /**
     * this调用导致事务失效解决方案1：
     * 自己注入自己，@Lazy循环依赖
     * */
    @Autowired
    @Lazy
    private  IUserCouponService userCouponService;

    @Override
    public List<Coupon> getAvailableByUserId(Long userId) {
        return this.baseMapper.getAvailableByUserId(userId);
    }

    @Override
    public PageDTO<CouponVO> queryMyCouponPage(UserCouponQuery query) {
        // 1.获取当前用户
        Long userId = UserContext.getUser();
        // 2.分页查询用户券
        Page<UserCoupon> page = lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .eq(UserCoupon::getStatus, query.getStatus())
                .page(query.toMpPage(new OrderItem("term_end_time", true)));
        List<UserCoupon> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }

        // 3.获取优惠券详细信息
        // 3.1.获取用户券关联的优惠券id
        Set<Long> couponIds = records.stream().map(UserCoupon::getCouponId).collect(Collectors.toSet());
        // 3.2.查询
        List<Coupon> coupons = couponMapper.selectBatchIds(couponIds);

        // 4.封装VO
        return PageDTO.of(page, BeanUtils.copyList(coupons, CouponVO.class));
    }

    @Override
    public void exchangeCoupon(String code) {
        Long userId = UserContext.getUser();
        // 解析code， CodeUtil
        long serialNum = CodeUtil.parseCode(code);
        // 判断是否已经兑换过了,Bitmap
        Boolean exists = redisTemplate.opsForValue().setBit(EXCHANGE_CODE_BITMAP, serialNum, true);
        if(exists){
            // 是，return
            throw new BizIllegalException("不能重复兑换");
        }
        // 查询兑换码
        ExchangeCode exchangeCode = codeService.lambdaQuery().eq(ExchangeCode::getCode, code).one();
        // 如果不存在，return
        if(exchangeCode == null){
            throw new BizIllegalException("兑换码不存在");
        }
        Long couponId = exchangeCode.getExchangeTargetId();
        // 判断是否过期
        LocalDateTime now = LocalDateTime.now();
        if(exchangeCode.getExpiredTime().isBefore(now)){
            // 如果已经过期，return
            throw new BizIllegalException("兑换码已经过期");
        }
        // 查询coupon
        Coupon coupon = couponService.getById(couponId);
        if(coupon == null){
            throw new BizIllegalException("优惠券不存在");
        }
        // 判断是否超出限领数量，查询user_coupon
        //   查询用户已经领取的券的数量
        Integer count = this.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .eq(UserCoupon::getCouponId, couponId)
                .count();
        if(count >= coupon.getUserLimit()){
            // 如果超出，return
            throw new BizIllegalException("优惠券超出每人限领数量");
        }
        // update coupon set issue_num = issue_num+1 where id = #{couponId}
        couponService.lambdaUpdate().setSql("issue_num = issue_num + 1")
                .eq(Coupon::getId, couponId).update();
        // insert user_coupon
        UserCoupon uc= new UserCoupon();
        uc.setUserId(userId);
        uc.setCouponId(couponId);
        if(coupon.getTermDays() != null && coupon.getTermDays() > 0){
            uc.setTermBeginTime(LocalDateTime.now());
            uc.setTermEndTime(LocalDateTime.now().plusDays(coupon.getTermDays()));
        }else{
            uc.setTermBeginTime(coupon.getTermBeginTime());
            uc.setTermEndTime(coupon.getTermEndTime());
        }
        uc.setStatus(1);
        uc.setCreateTime(LocalDateTime.now());
        uc.setUpdateTime(LocalDateTime.now());
        this.save(uc);
        // update exchange_code set user_id = #{userId}, status = 2
        codeService.lambdaUpdate().set(ExchangeCode::getUserId, userId)
                .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED.getValue())
                .eq(ExchangeCode::getId, exchangeCode.getId())
                .update();
    }

//    @Autowired
//    RedisLock redisLock;

    @Autowired
    private RedissonClient redissonClient;


    // Spel表达式
    @Override
    // @Lock(name="redisson-lock:#{couponId}")
    public void receive(Long couponId) {
        // 获取用户id
        Long userId = UserContext.getUser();
        // 根据couponId查询coupon
        Coupon coupon = getCoupon(couponId);
        // coupon不存在，return
        if(coupon == null){
            throw new BizIllegalException("优惠券不存在");
        }
        // 判断库存是否充足，issue_num《 total_num
        Integer issueNum = coupon.getIssueNum();
        Integer totalNum = coupon.getTotalNum();
        if(issueNum >= totalNum){
            // 没有库存，return
            throw new BizIllegalException("优惠券库存不足");
        }
        // 是否超出限领数量,redis
        String key = USER_COUPON_COUNT.replaceFirst("\\{\\}", ""+userId).replaceFirst("\\{\\}", ""+couponId);
        Long count = redisTemplate.opsForValue().increment(key);
        // 如果超出，return
        if(count > coupon.getUserLimit()){
            throw new BizIllegalException("优惠券超出每人限领数量");
        }
        // 否则
        // 发消息
        UserCouponDTO msg = new UserCouponDTO();
        msg.setUserId(userId);
        msg.setCouponId(couponId);
        mqHelper.send(MqConstants.Exchange.PROMOTION_EXCHANGE, MqConstants.Key.COUPON_RECEIVE, msg);
    }

    private Coupon getCoupon(Long couponId){
        // 先查询缓存
        String key = COUPON + couponId;
        String json = redisTemplate.opsForValue().get(key);
        // 如果有，return
        if(StringUtils.isNotBlank(json)){
            return JsonUtils.toBean(json, Coupon.class);
        }
        // 如果没有，查数据库
        Coupon coupon = couponService.getById(couponId);
        if(coupon != null){
            // 存入redis
            redisTemplate.opsForValue().set(key, JsonUtils.toJsonStr(coupon));
        }
        return coupon;
    }
}
